package com.gistmap.ntdc.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gistmap.ntdc.exception.ErrorItems;
import com.gistmap.ntdc.exception.ServiceException;
import com.gistmap.ntdc.mq.Producer;
import com.gistmap.ntdc.persistence.domain.*;
import com.gistmap.ntdc.persistence.repo.MerchantOrderDetailRepository;
import com.gistmap.ntdc.persistence.repo.MerchantOrderRepository;
import com.gistmap.ntdc.util.Constant;
import com.gistmap.ntdc.util.SessionUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.gistmap.ntdc.persistence.domain.MerchantOrderStatus.PAID;
import static com.gistmap.ntdc.persistence.domain.MerchantOrderStatus.UNPAY;

/**
 * @author zhangran
 * @date 2018/12/20
 */
@Component
@Transactional
public class MerchantOrderService {
    @Autowired
    private MerchantOrderRepository merchantOrderRepository;
    @Autowired
    private MerchantOrderDetailRepository merchantOrderDetailRepository;
    @Autowired
    private ProductService productService;
    @Autowired
    private UserService userService;
    @Autowired
    private AddressService addressService;
    @Autowired
    private Producer producer;

    public MerchantOrder find(Long orderId){
        return merchantOrderRepository.findById(orderId).orElseThrow(() -> new ServiceException(ErrorItems.ORDER_NOT_FIND));
    }

    public void createOrder(OrderForm orderForm){
        // 构建快照,扣除库存
        TmpInfo info = generateInfo(orderForm);
        MerchantOrder order = new MerchantOrder();
        order.setInfo(JSON.toJSONString(info.info.values()));
        order.setUser(userService.find(SessionUtil.getUser().getId()));
        order.setStatus(UNPAY);
        order.setLatitude(orderForm.latitude);
        order.setLongitude(orderForm.longitude);
        order.setAmount(info.sum);

        // 保存地址信息
        if (orderForm.addressId != null) {
            Address address = addressService.find(orderForm.addressId);
            order.setRecipientAddress(address.getDetail());
            order.setRecipientMobile(address.getMobile());
            order.setRecipientName(address.getUserName());
        }
        merchantOrderRepository.save(order);

        // 保存订单详情
        List<MerchantOrderDetail> details = Lists.newArrayList();
        for (ProductForm productForm : orderForm.products) {
            JSONObject on = info.info.get(productForm.id);

            MerchantOrderDetail detail = new MerchantOrderDetail();
            detail.setMerchantOrder(order);
            detail.setCount(productForm.count);
            detail.setAmount((on.getIntValue("price") - on.getIntValue("discount")) * productForm.count);
            detail.setDiscount(on.getIntValue("discount") * productForm.count);
            detail.setMerchandiseSnapshot(on.toString());
            details.add(detail);
        }
        merchantOrderDetailRepository.saveAll(details);

        // 5分钟未付超时
        // 可以考虑在订单中加入支付方式
        // 微信支付->走微信支付->是否超时->微信通知
        // 会员支付->扣虚拟账户->短信通知
        producer.delaySend(order.getId().toString(), "order.queue", Constant.TIME_OF_PAY);

    }

    private TmpInfo generateInfo(OrderForm orderForm) {
        List<ProductForm> products = orderForm.products;
        int sum = 0;
        int totalDiscount = 0;
        if (products.size() >= 1) {
            Map<Long, JSONObject> map = Maps.newHashMap();
            for (ProductForm product : products) {
                // 减去相应的库存
                productService.operateStock(product.id, product.count , false);

                Product p = productService.find(product.id);
                JSONObject on = new JSONObject();
                on.put("name", p.getName());
                on.put("description", p.getDescription());
                on.put("image", p.getImageUrl());
                on.put("price", p.getPrice());
                on.put("discount", p.getDiscount());
                sum = sum + ( product.count * (p.getPrice() - p.getDiscount()));
                totalDiscount = totalDiscount + (product.count - p.getDiscount());
                map.put(product.id, on);
            }
            return new TmpInfo(map, sum , totalDiscount);

        } else {
            throw new ServiceException(ErrorItems.NO_PRODUCT_SELECT);
        }
    }

    /**
     * mq回调 - 检查订单是否支付
     * @param orderId
     */
    public void unpayCheck(Long orderId){
        MerchantOrder order = find(orderId);
        if (order.getStatus() == UNPAY) {
            order.setStatus(MerchantOrderStatus.CANCEL);
            merchantOrderRepository.save(order);
        }
    }

    public void onPaySuccess(Long id, Integer amount) {
        MerchantOrder merchantOrder =  merchantOrderRepository.findOneWithWriteLock(id);
        if (merchantOrder.getStatus() == PAID) {
            throw new ServiceException(ErrorItems.ORDER_PAY_REPAT);
        }
        if (!merchantOrder.getAmount().equals(amount)) {
            throw new ServiceException(ErrorItems.AMOUNT_NOT_CORRECT);
        }
        merchantOrder.setPaymentTime(new Date());
        merchantOrder.setStatus(MerchantOrderStatus.PAID);
        merchantOrderRepository.save(merchantOrder);

        // todo 微信推送
    }

    @Data
    public static class OrderForm{
        public List<ProductForm> products;
        public String longitude;
        public String latitude;
        @NotNull
        public Long addressId;
    }

    @Data
    public static class ProductForm{
        public Long id;
        public int count;
        public int price;
    }

    @Data
    public static class TmpInfo{

        int sum;
        int totalDiscount;
        Map<Long, JSONObject> info;

        TmpInfo(Map<Long, JSONObject> info, int sum, int totalDiscount) {
            this.info = info;
            this.sum = sum;
            this.totalDiscount = totalDiscount;
        }
    }
}
